import qs from "qs";
import * as auth from "auth-provider";
import { useAuth } from "context/auth-context";

const apiUrl = process.env.REACT_APP_API_URL;

interface Config extends RequestInit {
  token?: string;
  data?: object;
}

/**
 * 请求方法
 * @param endpoint 请求地址，如:/api/users, users就是endpoint
 * @param param1 fetch 相关配置
 * {
 *  @param data 参数
 *  @param token token
 *  @param headers 请求头信息
 *  @param customConfig 自定义参数
 * }
 */
export const http = async (
  endpoint: string,
  { data, token, headers, ...customConfig }: Config = {}
) => {
  const config = {
    method: "GET",
    headers: {
      Authorization: token ? `Bearer ${token}` : "",
      "Content-Type": data ? "application/json" : "",
    },
    ...customConfig,
  };

  if (config.method.toUpperCase() === "GET") {
    endpoint += `?${qs.stringify(data)}`;
  } else {
    config.body = JSON.stringify(data || {});
  }

  // fetch 只有在网络连接失败或者断网时才抛出异常，请求产生的401或500时并不会抛出异常
  // 所以需要通过response.ok判断是否请求失败，失败时手动抛出异常
  // 而 axios 库则可以请求产生错误时抛出异常，通过catch捕获
  return window
    .fetch(`${apiUrl}/${endpoint}`, config)
    .then(async (response) => {
      if (response.status === 401) {
        await auth.logout();
        window.location.reload();
        return Promise.reject({ message: "请重新登录" });
      }
      const data = await response.json();
      if (response.ok) {
        return data;
      } else {
        return Promise.reject(data);
      }
    });
};

// 编写useHttp hook来帮我们接收token，不用我们手动传入http
export const useHttp = () => {
  const { user } = useAuth();

  // 这里传入的参数类型跟http方法传入的参数类型是一样的，使用ts的Parameters操作符简化一下
  // 这个Parameters操作符在后面会介绍
  return (...[endpoint, config]: Parameters<typeof http>) =>
    http(endpoint, { ...config, token: user?.token });
};
